package com.bill99.rpc.cluster.loadbalance;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.bill99.rpc.cluster.ServiceNode;
import com.bill99.rpc.common.AtomicPositiveInteger;
import com.bill99.rpc.registry.url.ReferenceUrl;
import com.bill99.rpc.registry.url.ServiceUrl;

/**
 * 加权轮询：该算法中，每个机器接受的连接数量是按权重比例分配的
 * 这是对普通轮询算法的改进
 * @author sheng.zhang
 */
public class RoundRobinLoadBalance extends AbstractLoadBalance {

	private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();

	private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();

	@Override
	public ServiceNode getInvokeServiceNode(List<ServiceNode> serviceNodes,
			ReferenceUrl referenceUrl) {
		String key = referenceUrl.toString();
		int length = serviceNodes.size();// 总个数
		int maxWeight = 0; // 最大权重
		int minWeight = Integer.MAX_VALUE; // 最小权重
		for (int i = 0; i < length; i++) {
			int weight = getWeight(serviceNodes.get(i).getServiceUrl());
			maxWeight = Math.max(maxWeight, weight); // 累计最大权重
			minWeight = Math.min(minWeight, weight); // 累计最小权重
		}
		if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
			AtomicPositiveInteger weightSequence = weightSequences.get(key);
			if (weightSequence == null) {
				weightSequences.putIfAbsent(key, new AtomicPositiveInteger());
				weightSequence = weightSequences.get(key);
			}
			int currentWeight = weightSequence.getAndIncrement() % maxWeight;
			List<ServiceNode> weightServiceNodes = new ArrayList<ServiceNode>();
			for (ServiceNode node : serviceNodes) { // 筛选权重大于当前权重基数的ServiceUrl
				ServiceUrl url = node.getServiceUrl();
				if (getWeight(url) > currentWeight) {
					weightServiceNodes.add(node);
				}
			}
			int weightLength = weightServiceNodes.size();
			if (weightLength == 1) {
				return weightServiceNodes.get(0);
			} else if (weightLength > 1) {
				serviceNodes = weightServiceNodes;
				length = serviceNodes.size();
			}
		}
		AtomicPositiveInteger sequence = sequences.get(key);
		if (sequence == null) {
			sequences.putIfAbsent(key, new AtomicPositiveInteger());
			sequence = sequences.get(key);
		}
		// 取模轮循
		return serviceNodes.get(sequence.getAndIncrement() % length);
	}

}
